from django.conf import settings
from django.db import models, connection, transaction
from django.contrib.auth.models import User
import random, re, os.path, datetime
from django.utils.html import escape


#Number of mesages per page display in MessageBox
MESSAGE_PER_PAGE = 40
#Number of items per page display in Inventory
ITEM_PER_PAGE = 25


class RpgAdmin(models.Model):
	id = models.IntegerField(primary_key=True)

	@staticmethod
	def create(username, first_n, last_n, password):
		#add a user
		try:
			u = User.objects.create_user(username, 'lennon@thebeatles.com', password)
		except:
			print >> sys.stderr, "Could not create rpgAdmin : username already exist\n"
			return
		u.first_name = first_n
		u.last_name = last_n
		u.save()
		#add an rpgAdmin
		a = RpgAdmin()
		a.id = u.id
		a.save()

class Master(models.Model):
	id = models.IntegerField(primary_key=True)
	gameId = models.IntegerField()

class LibraryItem(models.Model):
	gameId = models.IntegerField()
	label = models.CharField(max_length=250)
	path = models.CharField(max_length=250)
	description = models.TextField(null=True, blank=True)

class Message(models.Model):
	#id of the user that sent the message
	origId = models.IntegerField()
	#id of the destination, separated by ';'
	destId = models.CharField(max_length=500)
	body = models.TextField()
	date = models.DateTimeField(auto_now_add=True)
	#list of id of player that did not read this message
	unread = models.CharField(max_length=500, default="")


class Game(models.Model):
	name = models.CharField(max_length=250)
	iscap = models.IntegerField(null=True)

	#default Game Object print out
	def __unicode__(self):
		return self.name

class InventoryItem(models.Model):
	name = models.CharField(max_length=250)
	description = models.TextField()
	quantity = models.IntegerField()
	userId = models.IntegerField()


class CharacterSheet(models.Model):
	player_id = models.IntegerField(primary_key=True)
	busy_with = models.TextField(default="", null=True, blank=True)
	location_time = models.TextField(default="", null=True, blank=True)
	general_carac = models.TextField(default="", null=True, blank=True)
	carac_reaction = models.TextField(default="", null=True, blank=True)
	advantage_disadvantage = models.TextField(default="", null=True, blank=True)
	skills = models.TextField(default="", null=True, blank=True)
	defense = models.TextField(default="", null=True, blank=True)
	movement = models.TextField(default="", null=True, blank=True)
	dark_corner = models.TextField(default="", null=True, blank=True)


class Player(models.Model):
	id = models.IntegerField(primary_key=True)
	gameId = models.IntegerField()


class Notes(models.Model):
	player_id = models.IntegerField(primary_key=True)
	body = models.TextField(default="", null=True, blank=True)


class rpgEngineHelper():
	'''provide a set of usefull functions, casualy called in rpgEngine'''

	@staticmethod
	def hasRpgAccount(user):
		'''return true if the user has an rpgEngine account'''
		res = (rpgEngineHelper.getPlayer() != None) or (rpgEngineHelper.getMaster() != None) or (rpgEngineHelper.getAdmin() != None)
		return res

	@staticmethod
	def getPlayer(user):
		'''return true if the user has an rpgEngine Player account'''
		p = None
		try:
			p = Player.objects.get(id__exact=user.id)
		except Player.DoesNotExist:
			p = None
		return p

	@staticmethod
	def getMaster(user):
		'''return true if the user has an rpgEngine Master account'''
		m = None
		try:
			m = Master.objects.get(id__exact=user.id)
		except Master.DoesNotExist:
			m = None
		return m

	@staticmethod
	def getAdmin(user):
		'''return true if the user has an rpgEngine Admin account'''
		a = None
		try:
			a = RpgAdmin.objects.get(id__exact=user.id)
		except RpgAdmin.DoesNotExist:
			a = None
		return a

	@staticmethod
	def getGameId(user):
		'''return the game id of the current user, -1 else'''
		id = -1
		p = rpgEngineHelper.getPlayer(user)
		m = rpgEngineHelper.getMaster(user)
		if m != None :
			id = m.gameId
		if p != None :
			id = p.gameId
		return id

	@staticmethod
	def getMenuList(user):
		'''return the list of menu items, depending on user group (Player, Master or Admin)'''
		menu = []
		if rpgEngineHelper.getPlayer(user) != None :
			menu.append(["Messages","/messageBox/"])
			menu.append(["Character","/character/"])
			menu.append(["Inventory","/inventory/"])
			menu.append(["Notes","/notes/"])
			menu.append(["Team","/team/"])
			menu.append(["Library","/library/"])
			menu.append(["Logout","/logout/"])
		else :
			if rpgEngineHelper.getMaster(user) != None :
				menu.append(["Messages","/messageBox/"])
				menu.append(["Team","/team/"])
				menu.append(["Notes","/notes/"])
				menu.append(["Library","/library/"])
				menu.append(["Logout","/logout/"])
			else :
				if rpgEngineHelper.getAdmin(user) != None :
					menu.append(["Logout","/logout/"])
		return menu


	@staticmethod
	def getAddressSuggestion(user):
		'''return a string of player names separated by \\n'''
		userGroup = ""
		if rpgEngineHelper.getPlayer(user) != None :
			userGroup = "Player"
		else :
			if rpgEngineHelper.getMaster(user) != None :
				userGroup = "Master"
		cursor = connection.cursor()
		query = ""
		if (userGroup == "Player"):
			#select all users from the current user's game, but the current user
			query =  "SELECT auth_user.first_name, auth_user.last_name"+\
				" FROM auth_user, rpgEngine_player"+\
				" WHERE auth_user.id = rpgEngine_player.id"+\
				" AND rpgEngine_player.id != "+str(user.id)+\
				" AND auth_user.is_active = 1"+\
				" AND rpgEngine_player.gameId = (SELECT gameId FROM rpgEngine_player WHERE rpgEngine_player.id = "+str(user.id)+")"+\
				" ORDER BY auth_user.first_name, auth_user.last_name"
		else :
			#select all users from the current user's game
			query =  "SELECT auth_user.first_name, auth_user.last_name"+\
				" FROM auth_user, rpgEngine_player"+\
				" WHERE auth_user.id = rpgEngine_player.id"+\
				" AND auth_user.is_active = 1"+\
				" AND rpgEngine_player.gameId = (SELECT gameId FROM rpgEngine_master WHERE rpgEngine_master.id = "+str(user.id)+")"+\
				" ORDER BY auth_user.first_name, auth_user.last_name"
		cursor.execute(query)
		rows = cursor.fetchall()
		res_list = []
		for i in range(0,len(rows)):
			res_list.append(rows[i][0]+" "+rows[i][1])
		if (userGroup == "Player"):
			#add Master of the Game to suggested addresses
			query =  "SELECT auth_user.first_name, auth_user.last_name FROM auth_user, rpgEngine_master WHERE auth_user.id = rpgEngine_master.id AND rpgEngine_master.gameId = (SELECT gameId FROM rpgEngine_player WHERE rpgEngine_player.id = "+str(user.id)+")"
			cursor.execute(query)
			row = cursor.fetchone()
			res_list.insert(0, row[0]+" "+row[1])
		if len(res_list) == 0:
				return "nothing_at_all"
		return "\n".join(res_list)


	@staticmethod
	def executeTags(text):
		#cheater escape is probably not needed as < et > are replaced by &gt; and &lt; on form submit
		#Escape cheaters
		pattern = re.compile(r"(<strong><big>|<big><strong>)?[0-9]+(</strong></big>|</big></strong>)?\([0-9]+d[0-9]+(.(\+|-).[0-9]+)?\)")
		res = []
		l = len(text)
		cpt = 0
		ind = 0
		end = 0
		while cpt < l:
			#get next string to replace
			match = pattern.search(text[cpt:])
			if match == None :
				break
			ind = match.start()
			end = match.end()
			res.append(text[cpt:cpt+ind])
			#replace attempt of cheating by crossing it
			s = "<del>"+text[cpt+ind:cpt+end]+"</del>"
			s = s.replace("<strong>","")
			s = s.replace("</strong>","")
			s = s.replace("<big>","")
			s = s.replace("</big>","")
			res.append(s)
			cpt += end
		res.append(text[cpt:l])
		#Throw Dices
		text = "".join(res)
		res = []
		l = len(text)
		cpt = 0
		ind = 0
		end = 0
		base = 0
		multi = 1
		pattern = re.compile(r"@[0-9]*[d,D][0-9]+(\s*(\+|-)\s*[0-9]+)?")
		x = 0
		while cpt < l:
			#get next string to replace
			match = pattern.search(text[cpt:])
			if match == None :
				break
			ind = match.start()
			end = match.end()
			res.append(text[cpt:cpt+ind])
			#get the die base and multiplicator
			try :
				multi = int(match.group().lower()[1:match.group().find("d")])
			except ValueError :
				multi = 1
			#get an eventual modificator applied to the result of the dices
			modif = 0
			modifInd = match.group().lower().find("+")
			modifSign = 1
			if (modifInd == -1) :
				modifInd = match.group().lower().find("-")
				modifSign = -1
			if (modifInd == -1) :
				base = int(match.group()[(match.group().lower().find("d")+1):])
			else :
				base = int(match.group()[(match.group().lower().find("d")+1):modifInd])
				modif = int(match.group()[(modifInd + 1):])
			x = 0
			for i in range(0, multi) :
				x += random.randint(1, base)
			x += modif * modifSign
			res.append("<strong><big>"+str(x)+"</big></strong>")
			res.append("("+str(multi)+"d"+str(base))
			if (modifInd != -1) :
				res.append(match.group()[modifInd:])
			res.append(")")
			cpt += end
		res.append(text[cpt:l])
		return "".join(res)

	@staticmethod
	def execute_style_tag(text, gameId) :
		'''execute tags related to style
		 \'''Bold\'''
		 ///Italic///
		 ---Title---
		 TODO : >>>List Element, at the beginning of a line'''
		#Bold
		l = text.split("'''")
		tagStart = True
		cpt = 0;
		while cpt < (len(l) - 1) :
			if tagStart :
				#start tag
				if (cpt + 1) >= (len(l) - 1) :
					#don't insert a start tag which is not closed
					break
				tagStart = False
				cpt += 1
				l.insert(cpt, "<big><strong>")
			else:
				#end tag
				tagStart = True
				cpt += 1
				l.insert(cpt, "</strong></big>")
			cpt += 1;
		text = "".join(l)
		#Italic
		l = text.split("///")
		tagStart = True
		cpt = 0;
		while cpt < (len(l) - 1) :
			if tagStart :
				#start tag
				if (cpt + 1) >= (len(l) - 1) :
					#don't insert a start tag which is not closed
					break
				tagStart = False
				cpt += 1
				l.insert(cpt, "<i>")
			else:
				#end tag
				tagStart = True
				cpt += 1
				l.insert(cpt, "</i>")
			cpt += 1;
		text = "".join(l)
		#Title
		l = text.split("---")
		tagStart = True
		cpt = 0;
		while cpt < (len(l) - 1) :
			if tagStart :
				#start tag
				if (cpt + 1) >= (len(l) - 1) :
					#don't insert a start tag which is not closed
					break
				tagStart = False
				cpt += 1
				l.insert(cpt, "<big><big><big>")
			else:
				#end tag
				tagStart = True
				cpt += 1
				l.insert(cpt, "</big></big></big>")
			cpt += 1;
		text = "".join(l)
		#Link to Library
		res = []
		l = len(text)
		cpt = 0
		ind = 0
		end = 0
		base = 0
		multi = 1
		pattern = re.compile(r"\[\[.+\]\]")
		x = 0
		while cpt < l:
			#get next string to replace
			match = pattern.search(text[cpt:])
			if match == None :
				break
			ind = match.start()
			end = match.end()
			res.append(text[cpt:cpt+ind])
			val = text[cpt+ind+2:cpt+end-2]
			if val.startswith("http://") or val.startswith("https://"):
				#external link
				res.append("<a href=\"" +val+ "\" target=\"_blank\" >"+val+"</a>")
			else:
				#link to library item
				res.append("<a href=\"" + settings.MEDIA_URL + "games/" + str(gameId) + "/library/" +val+ "\" target=\"_blank\" >"+val+"</a>")
			cpt += end
		res.append(text[cpt:l])
		text = "".join(res)
		return text

	@staticmethod
	def uploadFileToLibrary(gameId, f) :
		'''upload a new file to the library of a Game. Add numeric extention to file name if name already used. Return the name under which the file has been saved'''
		#compute file name on server
		tmp = f.name
		l = tmp.split(".")
		nameBase = u".".join(l[0:-1])
		nameExt = u"." + l[-1]
		if (os.path.isfile(settings.MEDIA_ROOT + "games/" + str(gameId) + "/library/" + nameBase + nameExt)) :
			suffix = "1"
			cpt = 1
			while (os.path.isfile(settings.MEDIA_ROOT + "games/" + str(gameId) + "/library/" + nameBase + "_"  + suffix + nameExt)) :
				cpt += 1
				suffix = str(cpt)
			name = nameBase + "_" + suffix + nameExt
		else :
			name = nameBase + nameExt
		#write file
		destination = open(settings.MEDIA_ROOT + "games/" + str(gameId) + "/library/" + name, 'wb+')
 		for chunk in f.chunks():
			destination.write(chunk)
		destination.close()
		return name

	@staticmethod
	def createGameStaticFolder(gameId) :
		'''Create necessary folders when a game is added : settings.MEDIA_ROOT/<game_id>/library/'''
		d = os.path.dirname(settings.MEDIA_ROOT + "games/" + str(gameId) + "/library/")
		os.makedirs(d)


	@staticmethod
	def getLibraryItemList(user):
		gameId = rpgEngineHelper.getGameId(user)
		cursor = connection.cursor()
		#select all Library items with gameId corresponding to current game
		query =  "SELECT id, label, path, description FROM rpgEngine_libraryitem WHERE rpgEngine_libraryitem.gameId = "+str(gameId)+" ORDER BY label"
		cursor.execute(query)
		l = cursor.fetchall()
		#iter results to add address prefix to file's path
		res = []
		for i in range(0, len(l)) :
			res.append([l[i][0], l[i][1], settings.MEDIA_URL + "games/" + str(gameId) + "/library/" + l[i][2], l[i][3]])
		return res


	@staticmethod
	def getMessages(user, beginAt="0"):
		'''return the list of messages to/written by the current user, empty list if none. 
		The list is joined with the Player and Master tables 
		in order to dislay a user friendly name for the message origin and destination'''
		cursor = connection.cursor()
		#fetch all messages sent by/to the logged on user
		query = "SELECT id, origId, destId, body, date, unread FROM rpgEngine_message"+\
				" WHERE origId = "+str(user.id)+" OR destId = "+str(user.id)+\
				" OR destId LIKE '"+str(user.id)+";%%'"+\
				" OR destId LIKE '%%;"+str(user.id)+"'"+\
				" OR destId LIKE '%%;"+str(user.id)+";%%'"+\
				" ORDER BY date DESC LIMIT "+beginAt+","+str(MESSAGE_PER_PAGE)
		cursor.execute(query)
		messages = cursor.fetchall()
		master = None
		if (rpgEngineHelper.getPlayer(user) != None) :#current user is a player
			#get all Players from the same game as [id, first_name, last_name]
			query = "SELECT rpgEngine_player.id, first_name, last_name FROM auth_user, rpgEngine_player "+\
				"WHERE auth_user.id = rpgEngine_player.id "+\
				"AND gameId = (SELECT gameId FROM rpgEngine_player WHERE id = "+str(user.id)+")"
			cursor.execute(query)
			players = cursor.fetchall()
			#get the Master as [id, first_name, last_name]
			query = "SELECT rpgEngine_master.id, first_name, last_name FROM auth_user, rpgEngine_master "+\
				"WHERE auth_user.id = rpgEngine_master.id "+\
				"AND gameId = (SELECT gameId FROM rpgEngine_player WHERE id = "+str(user.id)+")"
			cursor.execute(query)
			master = cursor.fetchone()
		else :#current user is a master
			#get all Players from the same game as [id, first_name, last_name]
			query = "SELECT rpgEngine_player.id, first_name, last_name FROM auth_user, rpgEngine_player "+\
				"WHERE auth_user.id = rpgEngine_player.id "+\
				"AND gameId = (SELECT gameId FROM rpgEngine_master WHERE id = "+str(user.id)+")"
			#return query
			cursor.execute(query)
			players = cursor.fetchall()
			#get the Master as [id, first_name, last_name]
			query = "SELECT rpgEngine_master.id, first_name, last_name FROM auth_user, rpgEngine_master "+\
				"WHERE auth_user.id = rpgEngine_master.id "+\
				"AND rpgEngine_master.id = "+str(user.id)
			cursor.execute(query)
			master = cursor.fetchone()
		#put Players and Master in one list as [id, first_name, last_name]
		users = []
		for i in range(0,len(players)):
			users.append(list(players[i]))
		if (master != None):
			users.append(list(master))
		res = []
		#replace the id in origine and destination by the name of the user
		for i in range(0,len(messages)):
			destList = messages[i][2].split(";")
			dest = ""
			orig = ""
			for j in range(0,len(users)):
				if (messages[i][1] == users[j][0]):
					#replace origine id per a name
					orig = users[j][1]+" "+users[j][2]
				for k in range(0,len(destList)):
					if (destList[k] == str(users[j][0])):
						if (len(dest) != 0):
							dest += ", "
						#replace destination id per a name
						dest += users[j][1]+" "+users[j][2]
			body = messages[i][3].replace('\n',"<br/>")
			#unread if id present in player unread property
			unreadList = messages[i][5].split(";")
			#isUnread = str(user.id) in unreadList
			class_header = "header_collapsed"
			class_content = "content_collapsed"
			if str(user.id) in unreadList :
				class_header = "header_collapsed_new"
			res.append([messages[i][0], orig, dest, body, messages[i][4], class_header, class_content]);
		return res

	@staticmethod
	def setMsgAsRead(user, msgId):
		m = Message.objects.get(id__exact=msgId)
		unreadList = m.unread.split(";");
		#remove the actual user
		try:
			unreadList.remove(str(user.id))
		except:
			pass
		m.unread = ";".join(unreadList)
		m.save()


	@staticmethod
	def getInventoryItems(user, beginAt="0"):
		'''return the list of Inventory Items own by the current user
		if beginAt == -1 return all inventory items of the player'''
		cursor = connection.cursor()
		if beginAt == "-1":
			#fetch all items own by current user
			query = "SELECT id, name, description, quantity FROM rpgEngine_inventoryitem"+\
				" WHERE userId = "+str(user.id)+\
				" ORDER BY name"
		else:
			#fetch selected items own by current user
			query = "SELECT id, name, description, quantity FROM rpgEngine_inventoryitem"+\
					" WHERE userId = "+str(user.id)+\
					" ORDER BY name LIMIT "+beginAt+","+str(ITEM_PER_PAGE)
		cursor.execute(query)
		items = cursor.fetchall()
		res = []
		#replace return carrier by html tag <br/>, and protect against javascript injection
		for i in range(0,len(items)):
			res.append([items[i][0], items[i][1], escape(items[i][2]).replace('\n',"<br/>").replace('  ',"&nbsp;&nbsp;"), items[i][3]])
		return res
		
	@staticmethod
	def getInventoryItems_team(gameId):
		'''return the list of Inventory Items own by the team of current user
		ie which has the master_id as owner
		All items are returned, one page only for Team chess'''
		cursor = connection.cursor()
		query = "SELECT auth_user.id FROM auth_user JOIN rpgEngine_master"+\
				" ON auth_user.id = rpgEngine_master.id"+\
				" WHERE rpgEngine_master.gameId = "+str(gameId)
		cursor.execute(query)
		master_id = cursor.fetchone()[0]
		#get game Master id
		#fetch all items own by Team (with game Master as owner)
		query = "SELECT id, name, description, quantity FROM rpgEngine_inventoryitem"+\
				" WHERE userId = "+str(master_id)+\
				" ORDER BY name"
		cursor.execute(query)
		items = cursor.fetchall()
		res = []
		#replace return carrier by html tag <br/>, and protect against javascript injection
		for i in range(0,len(items)):
			res.append([items[i][0], items[i][1], escape(items[i][2]).replace('\n',"<br/>").replace('  ',"&nbsp;&nbsp;"), items[i][3]])
		return res


	@staticmethod
	def getGamePlayerList(user):
		gameId = rpgEngineHelper.getGameId(user)
		cursor = connection.cursor()
		#select all players with gameId corresponding to current game
		query = "SELECT DISTINCT users.id, users.username, users.first_name, users.last_name, players.gameId "+\
				"FROM rpgEngine_player as players, auth_user as users "+\
				"WHERE players.gameId = "+str(gameId)+\
				" AND users.id = players.id"+\
				" ORDER BY users.first_name "
		cursor.execute(query)
		res = cursor.fetchall()
		return res


	@staticmethod
	def getGameList(user):
		cursor = connection.cursor()
		#select all games and corresponding Master account details
		query = "SELECT DISTINCT games.name, users.username, users.first_name, users.last_name "+\
				"FROM rpgEngine_master as masters, auth_user as users, rpgEngine_game as games "+\
				"WHERE masters.gameId = games.id "+\
				"AND users.id = masters.id"
		cursor.execute(query)
		res = cursor.fetchall()
		return res


	@staticmethod
	def getSelectPageTeam(user, game_id, selectedIndex):
		isMaster = False
		if rpgEngineHelper.getMaster(user) != None :
			#current user is player
			isMaster = True
		#create list of player for the drop down list
		cursor = connection.cursor()
		query = "SELECT auth_user.id, auth_user.first_name, auth_user.last_name FROM auth_user, rpgEngine_player"+\
					" WHERE rpgEngine_player.gameId = "+str(game_id)+\
					" AND rpgEngine_player.id = auth_user.id"+\
					" AND auth_user.is_active = 1"+\
					" ORDER BY first_name, last_name DESC"
		cursor.execute(query)
		player_list = cursor.fetchall()
		
		pageSelector = ["<select onchange=\"goToPage(this);\">",]
		if selectedIndex == -1 :
			pageSelector.append("<option selected=\"selected\" value='/team/' >chest</option>")
		else:
			pageSelector.append("<option value='/team/' >chest</option>")
		if isMaster :
			if selectedIndex == -2 :
				pageSelector.append("<option selected=\"selected\" value='/team/addPlayer/' >add a player</option>")
			else:
				pageSelector.append("<option value='/team/addPlayer/' >add a player</option>")
		pageSelector.append("<optgroup label=\"------\"></optgroup>")
		link_base = "/team/player/"
		if isMaster :
			link_base = "/team/playerForMaster/"
		for i in range(0, len(player_list)):
			if str(player_list[i][0]) == str(selectedIndex):
				pageSelector.append("<option selected=\"selected\" value='"+link_base+str(player_list[i][0])+"/' >"+player_list[i][1]+" "+player_list[i][2]+"</option>")
			else:
				pageSelector.append("<option value='"+link_base+str(player_list[i][0])+"/' >"+player_list[i][1]+" "+player_list[i][2]+"</option>")
		pageSelector.append("</select>")
		return "".join(pageSelector)


	@staticmethod
	def updatePresence(user):
		'''update user last_login timestamp'''
		#update user timestamp
		cursor = connection.cursor()
		query =  "UPDATE auth_user SET last_login = NOW() WHERE id = "+str(user.id)
		cursor.execute(query)


	@staticmethod
	def getStickyBox(game_id):
		'''return the sticky box list ; (ISCAP, (connected player list, ...))'''
		#does not work for master
		cursor = connection.cursor()
		query = "SELECT u.first_name, u.last_name"+\
				" FROM auth_user as u, rpgEngine_player as p"+\
				" WHERE u.id = p.id"+\
				" AND p.gameId = "+str(game_id)+\
				" AND TIMESTAMPDIFF(MINUTE, u.last_login, NOW()) < 15"+\
				" ORDER BY u.first_name"
		cursor.execute(query)
		tmp = cursor.fetchall()
		l = []
		for i in range(0, len(tmp)):
			l.append(tmp[i])
		query = "SELECT u.first_name, u.last_name"+\
				" FROM auth_user as u, rpgEngine_master as m"+\
				" WHERE u.id = m.id"+\
				" AND m.gameId = "+str(game_id)+\
				" AND TIMESTAMPDIFF(MINUTE, u.last_login, NOW()) < 15"+\
				" ORDER BY u.first_name"
		cursor.execute(query)
		m = cursor.fetchone()
		if m != None:
			l.insert(0, m)
		g = Game.objects.get(id__exact=game_id)
		res = (str(g.iscap), l)
		return res




